/**
 * Dice.java
 *
 * @author Beatrice Bacelli
 * @author Luca Bedogni
 * @author Silvia Righini
 */
package unibo.lsb.logic;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * A couple of dice.
 */
public class Dice implements Serializable {

	int firstDice;
	int secondDice;

	/**
	 * @return the firstDice
	 */
	public int getFirstDice() {
		return firstDice;
	}

	/**
	 * @return the secondDice
	 */
	public int getSecondDice() {
		return secondDice;
	}

	/**
	 * Constructor. Dice are generated randomly.
	 */
	public Dice() {
		super();
		// seed is chosen automatically by Java
		Random generator = new Random();
		int d1 = cast(generator);
		int d2 = cast(generator);
		assignNormalized(d1, d2);
	}

	/**
	 * In case you want to set the dice values by yourself. Correct order is
	 * assigned by the method.
	 * 
	 * @param d1
	 * @param d2
	 */
	public Dice(int d1, int d2) {
		super();
		if ((d1 > 6) || (d1 < 1)) {
			throw new IllegalArgumentException("Range error: value " + d1
					+ " for first dice is not valid.");
		}
		if ((d2 > 6) || (d2 < 1)) {
			throw new IllegalArgumentException("Range error: value " + d1
					+ " for first dice is not valid.");
		}
		assignNormalized(d1, d2);
	}

	/**
	 * Orders the dice so that the bigger one is always the first.
	 * 
	 * @param d1
	 * @param d2
	 */
	private void assignNormalized(int d1, int d2) {
		// normalize
		if (d1 >= d2) {
			this.firstDice = d1;
			this.secondDice = d2;
		} else {
			this.firstDice = d2;
			this.secondDice = d1;
		}
	}

	/**
	 * Checks whether the bigger dice is the first one. Swaps them if not.
	 */
	private void normalize() {
		if (this.secondDice > this.firstDice) {
			int tmp = this.firstDice;
			this.firstDice = this.secondDice;
			this.secondDice = tmp;
		}
	}

	/**
	 * Casts 1 dice UNUSED ?
	 * 
	 * @return random value between 1 and 6
	 */
	// private int cast() {
	// return getRandomInt(1, 6, new Random());
	// }
	/**
	 * Casts 1 dice
	 * 
	 * @param aGenerator
	 *            seed
	 * @return integer random value between 1 and 6
	 */
	private int cast(Random aGenerator) {
		return getRandomInt(1, 6, aGenerator);
	}

	/**
	 * @param a
	 *            lower value of the range
	 * @param b
	 *            upper value of the range
	 * @param aGenerator
	 *            seed
	 * @return integer random value between a and b
	 */
	private static int getRandomInt(int a, int b, Random aGenerator) {
		if (a > b) {
			throw new IllegalArgumentException(
					"Range error: start exceedes end.");
		}
		int randomNumber = aGenerator.nextInt(b) + 1;
		return (randomNumber);
	}

	/**
	 * This function checks if the dice is equal or bigger than the given dice.
	 * It is supposed to be used when doubting the dice passed by the previous
	 * player: they should be equal or greater than the value declared by the
	 * player.
	 * 
	 * @param d
	 *            dice for comparison
	 * @return
	 */
	public boolean isEqualOrBigger(Dice d) {
		this.normalize();
		d.normalize();
		/* i check if this is equal or bigger than d */
		boolean b = false;

		/* case number are identical */
		if (this.equals(d)) {
			b = true;
		} else if (this.isBiggerThan(d)) {
			b = true;
		}
		return b;
	}

	/**
	 * Function is supposed to be used when checking that a player is passing a
	 * value that is greater than the one that was passed to him. NOTE: this
	 * will be used if we allow players to write a value instead of picking it
	 * among valid values with a radio button..
	 * 
	 * @param d
	 * @return
	 */
	public boolean isBiggerThan(Dice d) {

		boolean b = false;

		if (this.is21()) {
			/* 21 also wins against 21 so 21 bigger than 21 somehow.. */
			b = true;
		} else if (d.is21()) {
			/* if d is 21, we're done */
			b = false;
		} else if (this.isPair()) {
			/* checking pair against pair */
			if (d.isPair()) {
				/* ex. 44 against 22 just a comparison needed */
				if (this.getFirstDice() > d.getFirstDice()) {
					b = true;
				}
			} else {
				/* if this is pair and d is not, clearly this bigger than d */
				b = true;
			}
		}
		/* this is not pair */
		else {
			if (!d.isPair()) {
				/* checking 'not pair' against not pair */
				if (this.getFirstDice() > d.getFirstDice()) {
					/* es. 65 > 43, just comparison on first digit needed */
					b = true;
				} else if (this.getFirstDice() == d.getFirstDice()) {
					if (this.getSecondDice() > d.getSecondDice()) {
						/* es. 63 > 64 comparison on both digits */
						b = true;
					}
					/* 63 !> 63, second digit not >, and b stays false */
				} /* first digit not > and b stays false as well */
			}
		}
		return b;
	}

	/**
	 * Computes the ordered list of all the values bigger than the given one
	 * 
	 * @return the lists
	 */
	public List<Dice> getBiggerEqValues() {
		List<Dice> l = new ArrayList<Dice>();
		if (this.isNotPair()) {
			l = this.getNotPairGE(l, true);
			Dice doubleOne = new Dice(1, 1);
			l = doubleOne.getAllPairBiggerEqual(l, true);
		} else if (this.isPair()) {
			l = this.getAllPairBiggerEqual(l, true);
		}
		l.add(new Dice(2, 1));
		return l;
	}

	/**
	 * Generates a list of all values bigger than the value of the dice.
	 * 
	 * @return
	 */
	public List<Dice> getBiggerValues() {
		List<Dice> l = new ArrayList<Dice>();
		if (this.isNotPair()) {
			l = this.getNotPairGE(l, false);
			Dice doubleOne = new Dice(1, 1);
			l = doubleOne.getAllPairBiggerEqual(l, true);
		} else if (this.isPair()) {
			l = this.getAllPairBiggerEqual(l, false);
		}
		l.add(new Dice(2, 1));
		return l;
	}

	/**
	 * Method returns all values not pair (and not 21) greater or equal than
	 * current dice value.
	 * 
	 * @param l
	 *            list to append the values to
	 * @return
	 */
	private List<Dice> getNotPairGE(List<Dice> l, boolean includeSame) {
		if (this.isPair()) {
			throw new IllegalArgumentException("Dice should not be pair.");
		}
		if (includeSame) {
			l.add(this);
		}
		int i = this.getFirstDice();
		int j = this.getSecondDice();
		j++;
		while (i <= 6) {
			while (j < i) {
				l.add(new Dice(i, j));
				j++;
			}
			j = 1;
			i++;
		}
		return l;
	}

	/**
	 * Method returns all values pair greater or equal than current dice value
	 * 
	 * @param l
	 *            list to append the values to
	 * @return
	 */
	private List<Dice> getAllPairBiggerEqual(List<Dice> l, boolean includeSame) {
		if (!this.isPair()) {
			throw new IllegalArgumentException("Dice should be pair.");
		}
		if (includeSame) {
			// adding the first dice itself
			l.add(this);
		}
		int value = this.getFirstDice();
		while (value < 6) {
			value++;
			l.add(new Dice(value, value));
		}
		return l;
	}

	/**
	 * Ad-hoc equals.
	 * 
	 * @param d
	 * @return
	 */
	public boolean equals(Dice d) {
		this.normalize();
		d.normalize();
		boolean b = false;
		if ((this.firstDice == d.getFirstDice())
				&& (this.secondDice == d.getSecondDice())) {
			b = true;
		}
		return b;
	}

	/**
	 * Checks whether dice value is 21.
	 * 
	 * @return
	 */
	public boolean is21() {
		if ((this.firstDice == 2) && (this.secondDice == 1)) {
			return true;
		} else
			return false;
	}

	/**
	 * Checks if the dice is a pair, like 11 or 44.
	 * 
	 * @return
	 */
	public boolean isPair() {
		if (this.firstDice == this.secondDice) {
			return true;
		} else
			return false;
	}

	/**
	 * This method differs from using !isPair because is does not consider 21 a
	 * 'not pair'.. so by checking if dice.notPair you also check that it is not
	 * 21.
	 * 
	 * @return boolean
	 */
	public boolean isNotPair() {
		if (!this.isPair() && !this.is21()) {
			return true;
		} else
			return false;
	}

	/**
	 * Returns the minimum value for a dice, which is 31.
	 * 
	 * @return
	 */
	public static Dice getMinimumDice() {
		return new Dice(3, 1);
	}

	@Override
	public String toString() {
		String s1 = (new Integer(this.firstDice)).toString();
		String s2 = (new Integer(this.secondDice)).toString();
		return s1 + s2;
	}

}
